import {
	isMagnetURI
} from '../validator';

const config = require('../apiurl');
const yx2API = require('./lock.api.js').httpUrl
const http = require("../http/http")
const lockConsole = require("./lock.console.js")
const moment = require("../moment")

const LOCK_STATUS_NORMAL = 0 // 正常状态
const LOCK_STATUS_NO_LOCK = 1 // 没有门锁
const LOCK_STATUS_NO_BLUETOOTH = 2 // 没有蓝牙
const LOCK_STATUS_CONNECTING = 3 // 连接中
const LOCK_STATUS_OPENING = 4 // 开门中
const LOCK_STATUS_TIMEOUT = 5 // 倒计时中
const LOCK_STATUS_HOUSE_FREEZE = 6 // 房间已冻结
const LOCK_STATUS_HOLIDAY = 7 // 休假中
const LOCK_STATUS_SUCCEED = 8 // 开锁成功
const LOCK_STATUS_SEARCH_FAILED = 9 // 查找失败
const LOCK_STATUS_FAILED = 10 // 开锁失败
const LOCK_STATUS_BLACKLIST = 11 // 门锁黑名单
const LOCK_STATUS_OWEN_FEE = 12 // 门锁未激活

// 插件实例缓存时间，超过该时间自动销毁
const PLUGIN_CACHE_TIME = 30

// #ifdef MP
if (config.isNeedLock) {
	// var Plugin = requirePlugin('myPlugin');
	var createPlugin = requirePlugin('myPlugin')
	var Plugin = createPlugin()
}
// #endif

var currentLockInfoMap = new Map()
var shouldHideLoading = false

/**
 * 执行操作
 * @param {*} houseId 关联房间号
 * @param {*} isGuestRoom 是否是客房
 * @param {*} otherParams 其他附件参数
 */
const doOperate = (operateKey, houseId, isGuestRoom, otherParams, hideLoading) => {

	shouldHideLoading = hideLoading

	// 开锁
	if (operateKey === "openLock") {
		return openLock(houseId, isGuestRoom)
	} else if (operateKey === "openLockByRemote") {
		// 远程开门
		return openLockByRemote(houseId, isGuestRoom, otherParams, hideLoading)
	} else if (operateKey === "readDNAInfo") {
		return readDNAInfo(houseId, isGuestRoom)
	}
	// 同步系统时间
	else if (operateKey === "synchronizeLockSystemTime") {
		return synchronizeLockSystemTime(houseId, isGuestRoom, hideLoading)
	}
	// 添加门卡
	else if (operateKey === "addKey") {
		return addKey(houseId, isGuestRoom, otherParams)
	}
	// 添加门卡准备
	else if (operateKey === "addKeyPre") {
		return addKeyPre(houseId, isGuestRoom, otherParams)
	}
	// 删除门卡准备
	else if (operateKey === "removeKeyPre") {
		return removeKeyPre(houseId, isGuestRoom, otherParams)
	} else if (operateKey === "removeUserPre") {
		return removeUserPre(houseId, isGuestRoom, otherParams)
	}
	// 启用钥匙
	// otherParams中要传id
	else if (operateKey === "enableKey") {
		return enableKey(houseId, isGuestRoom, otherParams)
	}
	// 远程激活钥匙
	else if (operateKey === "activeKey") {
		return activeKey()
	}
	// 禁用钥匙
	// otherParams中要传id
	else if (operateKey === "disableKey") {
		return disableKey(houseId, isGuestRoom, otherParams)
	}
	// 删除门卡
	else if (operateKey === "removeKey") {
		return removeKey(houseId, isGuestRoom, otherParams)
	}
	// 删除设备
	else if (operateKey === "removeBluetoothDevice") {
		// 蓝牙删除钥匙
		return removeBluetoothDevice(houseId, isGuestRoom)
	} // 同步钥匙列表信息
	else if (operateKey === "synchronizeKeyList") {
		// 蓝牙删除钥匙
		return synchronizeKeyList(houseId, isGuestRoom)
	} // 激活钥匙
	else if (operateKey === "upgradeFirmware") {
		// lockConsole.log(`=========>开始执行升级操作【${moment().format("yyyy-MM-DD HH:mm:ss")}】`)
		// return upgradeFirmwareOTA(houseId, isGuestRoom, otherParams)

		if (otherParams && otherParams.chipType.toString() === "3") {
			return upgradeFirmwareDFU(houseId, isGuestRoom, otherParams)
		} else {
			return upgradeFirmwareOTA(houseId, isGuestRoom, otherParams)
		}
	} else if (operateKey === "cancelUpgradeFirmware") {
		// 升级固件版本
		return cancelUpgradeFirmware()
	}
}

/******************************* function method *******************************/

/**
 * 开门操作
 * @param {*} houseId 关联房间号
 * @param {*} isGuestRoom 是否是客房
 */
const openLock = (houseId, isGuestRoom) => {

	return new Promise((resolve, reject) => {

		var tlockInfo = null
		// 初始化门锁设备
		initLock(houseId, isGuestRoom)
			.then(({
				plugin,
				lockInfo
			}) => {

				tlockInfo = lockInfo
				if (!shouldHideLoading) {
					uni.showLoading({
						title: "开门中..."
					})
				}
				// 执行开锁操作
				return plugin.openLock()
			}).then((res) => {
				lockConsole.changeState(LOCK_STATUS_SUCCEED)
				lockConsole.log('openLock res -->', res);

				if (!shouldHideLoading) {
					uni.hideLoading()
					uni.showToast({
						title: '开门成功'
					});
				}

				if (resolve) {
					resolve()
				}

				// 如果锁具类型为4 执行同步门锁时间命令
				if (tlockInfo.lockType == 4) {
					setTimeout(function() {

						doOperate("synchronizeLockSystemTime", houseId, isGuestRoom)
					}, 500);
				}

				inner_operateLog(1, {
					...tlockInfo,
					power: res.power
				});
			})
			.catch(err => {

				lockConsole.log('openLock err -->', err);
				if (!shouldHideLoading) {
					uni.hideLoading()
				}

				if (reject) {
					reject()
				}
				// 上传失败日志
				inner_operateLog(2, tlockInfo);

				var errorMsg = `errCode:${err.errCode}, errMsg:${err.errMsg}`

				if (!shouldHideLoading) {
					uni.showToast({
						title: errorMsg,
						icon: "none"
					})
				}

				// 如果操作失败，就执行断开连接操作
				disconnect()

				lockConsole.changeState(LOCK_STATUS_FAILED, errorMsg)
			});
	})
}

/**
 * 同步门锁时间
 * @param {*} houseId 关联房间号
 * @param {*} isGuestRoom 是否是客房
 */
const synchronizeLockSystemTime = (houseId, isGuestRoom, hideLoading) => {
	return new Promise((resolve, reject) => {

		let tlockInfo = null
		// 初始化门锁设备
		initLock(houseId, isGuestRoom)
			.then(({
				plugin,
				lockInfo
			}) => {
				if (!shouldHideLoading && !hideLoading) {
					uni.showLoading({
						title: "开锁中..."
					})
				}
				tlockInfo = lockInfo

				// 执行开锁操作
				return plugin.synchronizeLockSystemTime()
			}).then((res) => {
				lockConsole.log('同步门锁时间 synchronizeLockSystemTime res -->', res);
				if (resolve) {
					resolve()
				}
			})
			.catch(err => {
				lockConsole.log(err)
				if (reject) {
					reject()
				}
			});
	})
}

/**
 * 读取设备DNA信息
 * @param {*} houseId 关联房间号
 * @param {*} isGuestRoom 是否是客房
 */
const readDNAInfo = (houseId, isGuestRoom) => {
	return new Promise((resolve, reject) => {

		let tlockInfo = null
		// 初始化门锁设备
		initLock(houseId, isGuestRoom)
			.then(({
				plugin,
				lockInfo
			}) => {

				// 执行开锁操作
				return plugin.readDNAInfo()
			}).then((res) => {
				lockConsole.log('readDNAInfo res -->', res);
				if (resolve) {
					resolve(res)
				}
			})
			.catch(err => {
				lockConsole.log(err)
				if (reject) {
					reject(err)
				}
			});
	})
}

/**
 * 同步钥匙列表信息
 * @param {*} houseId 关联房间号
 * @param {*} isGuestRoom 是否是客房
 */
const synchronizeKeyList = (houseId, isGuestRoom) => {
	return new Promise((resolve, reject) => {
		var keyList = []

		// 初始化门锁设备
		initLock(houseId, isGuestRoom)
			.then(({
				plugin,
				lockInfo
			}) => {

				plugin.on("synchronizeKeyList", function(data) {
					console.log('timestamp -->', Date.now(), ' synchronizeKeyList data -->',
						data)
					if (data.data.keyNum > 0) {
						keyList.push(data.data)
					}
					if (keyList.length == data.data.keyNum || data.data.keyNum == 0) {
						if (resolve) {
							resolve(keyList)
						}
					}
				});

				return plugin.synchronizeKeyList()
			}).then(res => {}).catch(err => {
				lockConsole.log("synchronizeKeyList err -->", err);
				if (reject) {
					reject(err)
				}
			});
	})
}

/**
 * 删除蓝牙设备
 * @param {*} houseId 
 * @param {*} isGuestRoom 
 */
const removeBluetoothDevice = (houseId, isGuestRoom) => {
	return new Promise((resolve, reject) => {
		initLock(houseId, isGuestRoom).then(({
			plugin,
			lockInfo
		}) => {
			return plugin.removeBluetoothDevice()
		}).then(res => {
			if (resolve) {
				resolve(res)
			}
		}, error => {
			if (reject) {
				reject(error)
			}
		})
	})
}

var addNumber = 0

/**
 * 录制门卡预处理
 * @param {*} houseId 
 * @param {*} isGuestRoom 
 * @param {*} otherParams 
 */
const addKeyPre = (houseId, isGuestRoom, otherParams) => {

	otherParams = {
		...otherParams,
		url: isGuestRoom ? yx2API.guestaddCardPre : yx2API.addCardPre
	}

	return initLock(houseId, isGuestRoom, otherParams)
}

/**
 * 录制门卡预处理
 * @param {*} houseId 
 * @param {*} isGuestRoom 
 * @param {*} otherParams 
 */
const removeKeyPre = (houseId, isGuestRoom, otherParams) => {

	var url = yx2API.delCardPre
	if(isGuestRoom){
		url = yx2API.delSingleKeyPre
	}
	otherParams = {
		...otherParams,
		url: url
	}

	return initLock(houseId, isGuestRoom, otherParams)
}

const removeUserPre = (houseId, isGuestRoom, otherParams) => {

	var url = ""
	if (isGuestRoom) {
		url = yx2API.guestdelHouseLockUserBluetoothPre
	} else {
		url = yx2API.delHouseLockUserBluetoothPre
	}

	otherParams = {
		...otherParams,
		url: url
	}

	return initLock(houseId, isGuestRoom, otherParams)
}



/**
 * 删除门卡
 */
const removeKey = (houseId, isGuestRoom, otherParams) => {

	return new Promise((resolve, reject) => {
		initLock(houseId, isGuestRoom).then(({
			plugin,
			lockInfo
		}) => {

			// 执行删除钥匙操作
			lockConsole.log(otherParams)
			
			plugin.on('report:removeKey', function(data) {
				var eventType = data.data.eventType;
				var eventData = data.data;
				var power = data.data.power;
				console.info('plugin is on remove key, data is ', data);
				
				if (resolve) {
					resolve({
						eventType,
						eventData,
						power
					})
				}
			});
			
			plugin.removeKey(otherParams).then(function(res) {
					lockConsole.log("removeKey res -->", res);

					// if (resolve) {
					// 	resolve(res)
					// }
				})
				.catch(function(err) {
					if (reject) {
						reject(err)
					}
				});
		})
	})
}

/**
 * 添加门卡
 * @param {*} houseId 
 * @param {*} isGuestRoom 
 */
const addKey = (houseId, isGuestRoom, otherParams) => {

	return new Promise((resolve, reject) => {
		initLock(houseId, isGuestRoom).then(({
			plugin,
			lockInfo
		}) => {

			// 初始化调用次数
			addNumber = 0

			var startDate = moment().format('yyyy-MM-DD HH:mm:ss')
			var endDate = moment().add("years", 10).format('yyyy-MM-DD HH:mm:ss')

			if (isGuestRoom && otherParams) {
				startDate = otherParams.startTime + ' 00:00:00';
				endDate = otherParams.endTime + ' 23:59:59';
			}

			var options2 = {
				keyGroupId: lockInfo.config.keyGroupId,
				usageCount: 255,
				type: 0,
				// 按内容添加
				keyType: 2,
				// 密码类型  2卡片
				validTimeMode: 0,
				// 时间模式为有效期类型
				validStartTime: new moment(startDate).toDate().getTime() / 1000,
				validEndTime: new moment(endDate).toDate().getTime() / 1000
			};

			// 录制卡片成功或失败回调
			plugin.on("report:addKey", function(data) {
				lockConsole.log("添加钥匙 -> plugin is on add key, data is ", data);
				if (data.errCode == '01') {
					uni.hideLoading()
					if (resolve) {
						resolve({
							data: data.data,
							lockInfo,
							addParams: options2
						})
					}
				} else {

					// 其他原因
					addNumber++
					if (addNumber >= 2) {

						// 第三次失败，直接放回
						uni.showToast({
							title: '录卡失败',
							icon: 'none'
						})
						if (reject) {
							reject(data)
						}
					}
				}
			});

			// 启动录卡操作
			plugin.addKey(options2).then(res => {
				lockConsole.log('addKey2 res -->', res);
			}, error => {
				lockConsole.log('addKey2 err -->', error);
				uni.showLoading({
					title: '请录卡'
				});
			})
		})
	})
}

/**
 * 启用钥匙
 */
const enableKey = (houseId, isGuestRoom, otherParams) => {
	return new Promise((resolve, reject) => {

		var tLockInfo = null
		initLock(houseId, isGuestRoom).then(({
			plugin,
			lockInfo
		}) => {
			tLockInfo = lockInfo
			console.log(lockInfo)
			var params = {
				mode: 1,
				value: true,
				lockKeyId: otherParams.id
			}
			if (otherParams.mode === 3) {
				// 添加门锁
				params = {
					mode: 3,
					keyGroupId: lockInfo.config.keyGroupId,
					value: true
				}
			}
			lockConsole.log("激活参数:", params)

			return plugin.enableKey(params)
		}).then(res => {
			lockConsole.log("激活门锁执行成功")
			lockConsole.changeState(LOCK_STATUS_NORMAL)
			if (resolve) {
				resolve({
					res,
					lockInfo: tLockInfo
				})
			}
		}).catch(error => {
			lockConsole.log("激活门锁执行失败")
			lockConsole.log(error)
			lockConsole.changeState(LOCK_STATUS_OWEN_FEE)
			if (reject) {
				reject(error)
			}
			disconnect()
		})
	})
}

/**
 * 禁用钥匙
 */
const disableKey = (houseId, isGuestRoom, otherParams) => {
	return new Promise((resolve, reject) => {

		var tLockInfo = null
		initLock(houseId, isGuestRoom).then(({
			plugin,
			lockInfo
		}) => {
			tLockInfo = lockInfo
			var params = {
				mode: 1,
				value: false,
				lockKeyId: otherParams.id
			}
			return plugin.enableKey(params)
		}).then(res => {
			if (resolve) {
				resolve({
					res,
					lockInfo: tLockInfo
				})
			}
		}).catch(error => {
			if (reject) {
				reject(error)
			}
		})
	})
}

/**
 * 激活钥匙
 */
const activeKey = () => {
	return new Promise((resolve, reject) => {
		var url = yx2API.tenantActivationLock + uni.getStorageSync("tenantId")
		http.request(url, {}, {
			loading: false,
			method: "get",
			failshowToast: true
		}).then(res => {
			if (resolve) {
				resolve(res)
			}
		}, error => {
			if (reject) {
				reject(error)
			}
		})
	})
}

var upgradeFileCache = {}

/**
 * 升级固件版本
 * 该插件旨在快速方便对蓝牙锁进行固件升级。
 * 该升级方式适用于2640及2642主控芯片的版本，
 * 即通用版本，L系列，P系列和LP系列的锁板。例如：HXJ01、HXJ01L、HXJ01LP、HXJ01P适用于这种升级方式。
 */
const upgradeFirmwareOTA = (houseId, isGuestRoom, otherParams) => {
	return new Promise((resolve, reject) => {

		var tLockInfo = null
		var tPlugin = null
		var tUpgradeRecordId = null
		var tUpgradeFile = null
		var tUpgradeFileUrl = null

		// 初始化门锁
		initLock(houseId, isGuestRoom).then(({
			plugin,
			lockInfo
		}) => {

			tLockInfo = lockInfo
			tPlugin = plugin
			var {
				upgradeFileUrl
			} = otherParams
			tUpgradeFileUrl = upgradeFileUrl
			lockConsole.log("加载升级文件")
			// 加载升级文件
			return loadUpgradeFileData(upgradeFileUrl)
		}).then(res => {
			tUpgradeFile = res.fileStream

			lockConsole.log("设置mtu")
			// 针对安卓升级慢的情况，先设置mtu再做升级
			return mutSetting(tPlugin)
		}).then(res => {
			lockConsole.log("执行升级")

			// 监听升级进度
			tPlugin.oad.on("oad:processing", (res) => {

				lockConsole.log(Date.now(), "oad:processing res -->", res);
			});
			// 监听升级成功
			tPlugin.oad.on("oad:success", (res) => {
				lockConsole.log("oad:success res -->", res);

				var params = {
					upgradeRecordId: tUpgradeRecordId,
					state: 2,
					hardwareVersionAfter: otherParams.hardwareVersionAfter,
					softwareVersionAfter: otherParams.softwareVersionAfter,
				}
				finishedUpgradeLog(params)

				if (resolve) {
					resolve(res)
				}
				clearTimer();
			});
			// 监听升级过程取消
			tPlugin.oad.on("oad:cancel", (res) => {
				lockConsole.log("oad:cancel res -->", res);
				if (reject) {
					reject(res)
				}
				clearTimer();
			});
			// 监听升级过程失败
			tPlugin.oad.on("oad:error", (err) => {
				lockConsole.log("oad:error err -->", res);

				var params = {
					upgradeRecordId: tUpgradeRecordId,
					state: 3,
					hardwareVersionAfter: otherParams.hardwareVersionAfter,
					softwareVersionAfter: otherParams.softwareVersionAfter,
				}
				finishedUpgradeLog(params)

				if (reject) {
					reject(err)
				}
				clearTimer();
			});
			tPlugin.oad.on("oad:MTU", (res) => {
				lockConsole.log("oad:MTU res -->", res);
				// 若MTU太小，则需要维持小程序与门锁的连接（参考“蓝牙SDK对接”的“维持连接”）
				// MTU: 一般来说，iOS为240，Android为16、20或其他较小值
				if (res.data.MTU < 240) {
					ping(tPlugin);
				}
			});

			lockConsole.log("tPlugin.oad.digest(tUpgradeFile);")
			tPlugin.oad.digest(tUpgradeFile);
			lockConsole.log("tPlugin.oad.update();")

			var startLogParams = {
				houseId: houseId,
				lockId: otherParams.lockId,
				lockMac: tLockInfo.config.lockMac,
				hardwareVersion: otherParams.hardwareVersion,
				softwareVersion: otherParams.softwareVersion,
				upgradeFile: tUpgradeFileUrl,
				upgradeVersionId: otherParams.upgradeVersionId
			}
			console.log(startLogParams)
			console.log("调用接口开始升级")
			startUpgradeLog(startLogParams).then(res => {
				console.log(res)
				tUpgradeRecordId = res.data
				lockConsole.log("oad start =>");
				tPlugin.oad.update();
			})
		}).catch(error => {
			console.log("错误异常")
			console.log(error)
			clearTimer()
			if (reject) {
				reject(error)
			}
		})
	})
}

/**
 * 升级固件版本
 * 该插件旨在快速方便对蓝牙锁进行固件升级。
 * 该升级方式适用于E系列锁板版本，例如：HXJ01E、HXJ32E、HXJ41E、HXJ51E和HXJ52E等适用于这种升级方式。
 */
const upgradeFirmwareDFU = (houseId, isGuestRoom, otherParams) => {
	return new Promise((resolve, reject) => {
		var tLockInfo = null
		var tPlugin = null
		var tUpgradeRecordId = null
		var tUpgradeFileUrl = null

		// 初始化门锁
		initLock(houseId, isGuestRoom).then(({
			plugin,
			lockInfo
		}) => {

			tLockInfo = lockInfo
			tPlugin = plugin
			var {
				upgradeFileUrl
			} = otherParams

			tUpgradeFileUrl = upgradeFileUrl

			lockConsole.log("加载升级文件")
			return loadUpgradeFileData(upgradeFileUrl)
		}).then(res => {
			lockConsole.log("解压升级文件")
			console.log(res)
			return unzipFileData(res.downLoadData)
		}).then(res => {
			console.log(res)

			lockConsole.log("执行升级")
			// 4.固件升级
			tPlugin.dfu.on("dfu:success", () => {
				lockConsole.log("dfu:success =>");
				var params = {
					upgradeRecordId: tUpgradeRecordId,
					state: 2,
					hardwareVersionAfter: otherParams.hardwareVersionAfter,
					softwareVersionAfter: otherParams.softwareVersionAfter,
				}
				finishedUpgradeLog(params)
				if (resolve) {
					resolve()
				}
			});
			tPlugin.dfu.on("dfu:error", (err) => {
				lockConsole.log("dfu:error err =>", err);
				var params = {
					upgradeRecordId: tUpgradeRecordId,
					state: 3,
					hardwareVersionAfter: otherParams.hardwareVersionAfter,
					softwareVersionAfter: otherParams.softwareVersionAfter,
				}
				finishedUpgradeLog(params)

				if (reject) {
					reject(err)
				}
			});
			tPlugin.dfu
				.init({
					initPacket: res.datFile,
					packet: res.binFile,
				})
				.then(() => {

					var startLogParams = {
						houseId: houseId,
						lockId: otherParams.lockId,
						lockMac: tLockInfo.config.lockMac,
						hardwareVersion: otherParams.hardwareVersion,
						softwareVersion: otherParams.softwareVersion,
						upgradeFile: tUpgradeFileUrl,
						upgradeVersionId: otherParams.upgradeVersionId
					}

					startUpgradeLog(startLogParams).then(res => {
						tUpgradeRecordId = res.data
						lockConsole.log("dfu start =>");
						tPlugin.dfu.update();
					})
				});
		}).catch(error => {
			console.log(error)
			if (reject) {
				reject(error)
			}
		})
	})
}

/**
 * 开始升级日志
 */
const startUpgradeLog = (params) => {
	return http.request(yx2API.startUpgrade, params, {
		method: 'post',
		loading: false
	})
}

/**
 * 结束升级日志
 */
const finishedUpgradeLog = (params) => {
	http.request(yx2API.finishUpgrade, params, {
		method: "post",
		loading: false
	}).then(res => {
		console.log(res)
	})
}

const upgradeLog = (result) => {

}

const cancelUpgradeFirmware = () => {
	if (currentPlugin) {
		currentPlugin.oad.cancel()
	}
}

/**
 * 加载升级文件
 */
const loadUpgradeFileData = (upgradeFileUrl) => {
	return new Promise((resolve, reject) => {
		lockConsole.log(upgradeFileUrl)
		if (!upgradeFileUrl) {
			lockConsole.log("升级文件路径不能为空")
			if (reject) {
				reject("升级文件路径不能为空")
			}
		}

		var data = upgradeFileCache[upgradeFileUrl]
		if (data) {
			if (resolve) {
				resolve(data)
			}
		} else {
			wx.downloadFile({
				url: upgradeFileUrl,
				success(res) {
					lockConsole.log("加载文件成功")
					if (res.statusCode === 200) {
						var fs = wx.getFileSystemManager();
						var tdata = fs.readFileSync(res.tempFilePath);
						if (tdata) {
							upgradeFileCache[upgradeFileUrl] = {
								fileStream: tdata,
								downLoadData: res
							}
							if (resolve) {
								resolve({
									fileStream: tdata,
									downLoadData: res
								})
							}
						} else {
							if (reject) {
								reject("升级文件加载失败")
							}
						}

					}
				},
				fail(error) {
					if (reject) {
						reject(error)
					}
				}
			});
		}
	})
}

/**
 * 解压文件
 */
const unzipFileData = (res) => {
	return new Promise((resolve, reject) => {
		var fs = wx.getFileSystemManager();
		var targetPath = `${wx.env.USER_DATA_PATH}/lockUpgradeFile`;
		fs.unzip({
			zipFilePath: res.tempFilePath,
			targetPath: targetPath,
			success() {
				fs.readdir({
					dirPath: targetPath,
					success(res) {
						console.log("解压成功")
						var mainfestJsonFile = res.files.find(filename => filename ===
							"manifest.json");
						var mainfest = fs.readFileSync(targetPath + "/" + mainfestJsonFile,
							"utf8");
						var mainfestJson = JSON.parse(mainfest);
						var {
							bin_file,
							dat_file
						} = mainfestJson.manifest.application;
						var datFilePath = targetPath + "/" + dat_file;
						var binFilePath = targetPath + "/" + bin_file;
						var datFile = fs.readFileSync(datFilePath);
						var binFile = fs.readFileSync(binFilePath);
						resolve({
							datFile,
							binFile,
						});
					},
					fail() {
						throw new Error("500005:固件数据读取失败");
					},
				});
			},
			fail() {
				throw new Error("500006:解压失败");
			},
		});
	})
}

/**
 * 针对安卓升级慢的情况，先设置mtu再做升级
 */
const mutSetting = (plugin) => {
	// 针对安卓升级慢的情况，先设置mtu再做升级
	new Promise(function(resolve, reject) {
		if (
			wx.getSystemInfoSync().system.toLowerCase().indexOf("android") ===
			-1
		) {
			/**
			 * iOS
			 */
			resolve();
		} else if (wx.canIUse("setBLEMTU")) {
			/**
			 * Android
			 */
			wx.setBLEMTU({
				deviceId: plugin.broadcastData.deviceId,
				mtu: 240,
				complete: resolve,
			});
		} else {
			/**
			 * wx.setBLEMTU不可用，使用原方案
			 */
			resolve();
		}
	})
}

let pingTimer = null;
const TIMEOUT = 10000; // ping心跳的间隔，该值小于设备自动断连的超时即可
// 清除计时
function clearTimer() {
	if (pingTimer) {
		clearTimeout(pingTimer);
		pingTimer = null;
	}
}
// 维持与门锁连接
function ping(plugin) {
	console.log("ping -->");
	clearTimer();
	pingTimer = setTimeout(() => {
		checkPluginInstanaceIsConnnected(plugin) &&
			plugin.synchronizeLockRecord({
				limit: 0
			}); // 与设备交互可以延长断开连接时间，这里发送一个包来刷新断连的超时，limit设为0即不获取日志
		ping(plugin);
	}, TIMEOUT);
}

function checkPluginInstanaceIsConnnected(plugin) {
	return plugin && plugin.connected;
}


/******************************* private method *******************************/

var currentPlugin = null // 当前插件对象
var currentPluginInitTime = null // 当前插件初始化时间
var currentPluginHouseLockKey = null // 当前插件对应的健值

/**
 * 获取设备信息
 */
const initLock = (houseId, isGuestRoom, otherParams) => {
	return new Promise((resolve, reject) => {

		// 先查锁具信息
		var tLockInfo = null
		inner_queryDeviceInfo(houseId, isGuestRoom, otherParams).then(lockInfo => {
			lockConsole.log("lockInfo", lockInfo)
			lockConsole.changeState(LOCK_STATUS_CONNECTING, "蓝牙搜索中...")

			tLockInfo = lockInfo

			if (!shouldHideLoading) {
				uni.showLoading({
					title: '蓝牙搜索中...'
				});
			}

			// 实例化或从缓存和中获取插件对象
			return initPluginWithCache(lockInfo, houseId)
		}).then(({
			plugin,
			isCache
		}) => {
			if (isCache) {
				if (!shouldHideLoading) {
					uni.hideLoading()
				}
				lockConsole.log("返回缓存plugin 对象")
				lockConsole.changeState(LOCK_STATUS_OPENING)
				if (resolve) {
					resolve({
						plugin,
						lockInfo: tLockInfo
					})
				}
			} else {
				console.log("============><============")
				console.log(plugin)
				plugin.on('ready', function(plugin) {
					lockConsole.changeState(LOCK_STATUS_OPENING)
					lockConsole.changeBlueState(true)
					lockConsole.log('plugin is on ready, state -->');
					console.log(plugin)
					if (!shouldHideLoading) {
						uni.hideLoading()
					}
					if (resolve) {
						resolve({
							plugin,
							lockInfo: tLockInfo
						})
					}
				});
				plugin.on('close', function(state) {
					if (!shouldHideLoading) {
						uni.hideLoading()
					}
					lockConsole.changeBlueState(false)
					lockConsole.log('plugin is on close, state -->', state);
					uni.closeBluetoothAdapter();
				});
				plugin.on('error', function(err) {
					lockConsole.log('plugin is on error -->', err);
					lockConsole.changeState(LOCK_STATUS_FAILED, err)

					if (reject) {
						reject(err)
					}
					if (!shouldHideLoading) {
						uni.hideLoading()
						uni.showToast({
							title: '搜索失败',
							icon: 'none'
						});
					}

					disconnect()
				});
			}
		})
	})
}

/**
 * 断开连接
 */
const disconnect = () => {
	return new Promise((resolve, reject) => {
		if (currentPlugin) {
			currentPlugin.disconnect().then(res => {
				lockConsole.log("===>断开连接")
				currentPluginInitTime = null
				currentPluginHouseLockKey = null
				currentPlugin = null
				if (resolve) {
					resolve()
				}

				uni.closeBluetoothAdapter();
			}, error => {
				lockConsole.log("===>连接断开失败：", error)
				currentPluginInitTime = null
				currentPluginHouseLockKey = null
				currentPlugin = null
				if (reject) {
					reject()
				}
			})
		}
	})
}

const connect = (houseId, isGuestRoom, longConnect, shouldHideLoading) => {
	shouldHideLoading = shouldHideLoading
	return new Promise((resolve, reject) => {
		initLock(houseId, isGuestRoom)
			.then(({
				plugin,
				lockInfo
			}) => {
				if (resolve) {
					resolve({
						plugin,
						lockInfo
					})
				}
			}, error => {
				if (reject) {
					reject(error)
				}
			})
	})
}

/**
 * 初始化插件对象，如果缓存中，则直接从缓存中获取
 * @param {*} lockInfo 
 * @param {*} houseId 
 */
const initPluginWithCache = (lockInfo, houseId) => {

	return new Promise((resolve, reject) => {
		var houseLockKey = houseId + "_" + lockInfo.config.lockMac
		var plugin = null
		var isCache = false


		var onInitCallBack = () => {

			// 重新打开
			inner_checkBluetooth().then(res => {

				// 重新初始化插件
				plugin = new Plugin({
					authCode: lockInfo.config.authCode,
					aesKey: lockInfo.config.aesKey,
					lockMac: lockInfo.config.lockMac,
					keyGroupId: lockInfo.config.keyGroupId
				})
				currentPlugin = plugin

				// 刷新上次使用时间
				currentPluginInitTime = moment()
				currentPluginHouseLockKey = houseId + "_" + lockInfo.config.lockMac

				if (resolve) {
					resolve({
						plugin,
						isCache
					})
				}
			})
		}

		// 房间和mac 地址相同切30秒之内
		if (houseLockKey === currentPluginHouseLockKey && currentPluginInitTime &&
			currentPluginInitTime.add(PLUGIN_CACHE_TIME, 'seconds').isSameOrAfter(moment())) {
			lockConsole.log("===>", "cache plugin")
			plugin = currentPlugin
			isCache = true

			// 刷新上次使用时间
			currentPluginInitTime = moment()
			currentPluginHouseLockKey = houseId + "_" + lockInfo.config.lockMac

			if (resolve) {
				resolve({
					plugin,
					isCache
				})
			}

		} else {

			lockConsole.log("===>", "new plugin")

			// 先关闭之前的链接
			if (currentPlugin !== null) {
				// 执行断开操作
				currentPlugin.disconnect().then(res => {
					onInitCallBack()
				}, error => {
					onInitCallBack()
				})
			} else {
				onInitCallBack()
			}
		}
	})
}

// /**
//  * 蓝牙连接缓存
//  */
// const initPluginCacheWatchDog = (timeAfter) => {

// 	lockConsole.log("plugin watch dog: 初始化")
// 	lockConsole.log("plugin watch dog: 间隔时间", timeAfter)
// 	lockConsole.log("plugin watch dog: 监控pluginKey", currentPluginHouseLockKey)

// 	var tPluginHouseLockKey = currentPluginHouseLockKey
// 	setTimeout(() => {

// 		if (!currentPlugin) {
// 			lockConsole.log("plugin watch dog: currentPlugin 为空")
// 			return
// 		}

// 		// 已切换锁具对象，不需要它处理，已另起监控
// 		if (tPluginHouseLockKey !== currentPluginHouseLockKey) {
// 			lockConsole.log("plugin watch dog: 已切换锁具对象，不需要它处理，已另起监控")
// 			return
// 		}

// 		if (!currentPluginInitTime) {

// 			// 如果初始化时间都没有 则直接销毁
// 			lockConsole.log("plugin watch dog: 初始化时间都没有 则直接销毁")
// 			currentPluginInitTime = null
// 			currentPluginHouseLockKey = null
// 			currentPlugin.disconnect()
// 			currentPlugin = null
// 		}

// 		if (currentPluginInitTime.add(timeAfter, 'seconds').isSameOrAfter(moment())) {

// 			// 时间之内
// 			lockConsole.log("plugin watch dog: 间隔时间内重新调用，进入下一次检测")
// 			initPluginCacheWatchDog(timeAfter)
// 		} else {

// 			// 如果回收间隔内未重新调用，则直接回收
// 			lockConsole.log("plugin watch dog: 间隔内未重新调用，则直接回收")
// 			currentPluginInitTime = null
// 			currentPluginHouseLockKey = null
// 			currentPlugin.disconnect()
// 			currentPlugin = null
// 		}
// 	}, timeAfter * 1000)
// }

/**
 * 获取门锁设备信息
 * @param {*} houseId 
 * @param {*} isGuestRoom 是否为客房
 */
const inner_queryDeviceInfo = (houseId, isGuestRoom, otherParams) => {
	console.log("houseId", houseId)
	var key = houseId + (isGuestRoom ? '_normal' : '_guest')

	return new Promise((resolve, reject) => {
		console.log("cache houseId:", houseId)
		console.log("key:", key)
		console.log(JSON.stringify(currentLockInfoMap))
		if (currentLockInfoMap.has(key)) {
			var lockInfo = currentLockInfoMap.get(key)
			console.log(lockInfo)
			if (lockInfo) {
				resolve(lockInfo)
				return
			}
		}

		// 通过接口获取锁具信息
		var optId = uni.getStorageSync('tenantId')
		var operatorType = 1
		if (!optId && uni.getStorageSync('userInfo')) {
			optId = uni.getStorageSync('userInfo').id
			operatorType = 2
		}
		var params = {
			houseId: houseId,
			optId: optId,
			type: operatorType
		}

		if (otherParams) {
			params = {
				...params,
				...otherParams,
			}
		}

		var url = ""
		if (otherParams && otherParams.url) {
			url = otherParams.url
		} else if (isGuestRoom) {
			url = yx2API.guestopenDoorByBluetooth;
		} else {
			url = yx2API.openDoorByBluetooth;
		}

		http.request(url, params, {
				loading: false,
				method: 'post',
				failshowToast: false,
			})
			.then((res) => {

				lockConsole.log("query lock info: ", res)

				var lockInfo = {
					config: {
						//"806fb051856b",
						lockMac: res.data.macAddr,
						aesKey: res.data.aesKey,
						//"lZq1xFOTUH0lKFYW",
						authCode: res.data.adminAuthCode,
						//"511047fe",
						keyGroupId: res.data.keyUserId
					},
					deviceCode: res.data.code,
					lockType: res.data.lockType ? res.data.lockType.value : null,
					imei: res.data.imei
				}

				currentLockInfoMap.set(key, lockInfo)
				resolve(lockInfo)
			}, error => {
				reject(error)
			});
	})
}

/**
 * 检查蓝牙状态
 */
const inner_checkBluetooth = () => {
	return new Promise((resolve, reject) => {
		isBluetoothAvalibale(() => {
			resolve()
		}, () => {
			reject()
		});
	})
}

/**
 * 检测当前手机蓝牙是否开启
 */
const isBluetoothAvalibale = (onAvaliable, onError) => {
	lockConsole.log("===>", "执行打开蓝牙操作")
	uni.openBluetoothAdapter({
		mode: "central",
		success: res => {
			lockConsole.log("===>蓝牙打开成功", res)
			if (onAvaliable != null) {
				onAvaliable();
			}
		},
		fail: error => {
			lockConsole.log("===>蓝牙打开失败", error)
			lockConsole.changeState(LOCK_STATUS_NO_BLUETOOTH)

			if (error.state == 4) {
				uni.showToast({
					title: '请打开手机蓝牙',
					icon: "none"
				});
			} else if (error.state == 3) {
				uni.showToast({
					title: '请授权微信访问蓝牙',
					icon: "none"
				});
			} else if (error.state == 2) {
				uni.showToast({
					title: '蓝牙不支持',
					icon: "none"
				});
			} else if (error.state == 1) {
				uni.showToast({
					title: '蓝牙重置中',
					icon: "none"
				});
			} else {
				lockConsole.log(error)
				uni.showToast({
					title: '当前蓝牙不可用',
					icon: "none"
				});
			}

			if (onError != null) {
				onError();
			}
		}
	});
};

/**
 * 记录操作日志
 * @param {*} openState 
 * @param {*} lockInfo 
 */
const inner_operateLog = (openState, lockInfo) => {
	var openDate = moment().format("yyyy-MM-DD HH:mm:ss");
	console.log("inner_operateLog", lockInfo)
	var params = {
		lockMac: lockInfo.config.lockMac,
		operKeyGroup: lockInfo.config.keyGroupId,
		power: lockInfo.power,
		openDate: openDate,
		state: openState
	};
	http.request(yx2API.opendoorlog, params, {
			loading: false,
			method: 'post',
			failshowToast: false
		})
		.then(
			(res) => {
				lockConsole.log('上报成功');
			},
			(err) => {
				lockConsole.log('上报失败');
			}
		);
}

/**
 * 远程开门
 * @param {*} houseId 
 */
const openLockByRemote = (houseId, isGuestRoom, otherParams, hideLoading) => {
	return new Promise((resolve, reject) => {
		var url = yx2API.openDoorByRemote
		var params = {
			houseId: houseId
		}
		lockConsole.changeState(LOCK_STATUS_CONNECTING)
		http.request(url, params, {
			method: 'post',
			loading: shouldHideLoading ? false : true,
			failshowToast: false
		}).then(res => {
			lockConsole.changeState(LOCK_STATUS_SUCCEED)
			if (resolve) {
				resolve()
			}
		}, err => {
			if (err && err.data) {
				lockConsole.log(JSON.stringify(err.data))
			}
			// 远程开门失败继续蓝牙开门
			if (otherParams && otherParams.continueBluetooth) {
				doOperate("openLock", houseId, isGuestRoom, otherParams, hideLoading).then(res => {
					if (resolve) {
						resolve()
					}
				}, error => {
					if (reject) {
						reject()
					}
				})
			} else {
				lockConsole.changeState(LOCK_STATUS_FAILED)
				if (reject) {
					reject()
				}
			}

		});
	})
}

const openDoorByRemote = (houseId, type, url) => {
	var that = this
	var con = '是否点击远程NB开门'
	if (type == 3 || type == 12) {
		con = '是否点击远程433开门'
	} else {
		con = '是否点击远程NB开门'
	}
	uni.showModal({
		title: '是否远程开门',
		content: con,
		success(res) {
			if (res.confirm) {
				// var url = url,//httpUrl.openDoorByRemote
				var params = {
					houseId: houseId
				}
				http.request(url, params, {
					method: 'post',
					loading: true,
					loadingText: '远程开门中...'
				}).then(res => {
					wx.showToast({
						title: res.msg,
					})
				}, err => {})
			} else if (res.cancel) {
				lockConsole.log('用户点击取消')
			}
		}
	})
}

const queryLockInfo = (houseId, isGuestRoom) => {
	return inner_queryDeviceInfo(houseId, isGuestRoom, null)
}

/**
 * 获取挂件版本简称
 * @param {升级文件全称} fullName 
 */
const getUploadFileShortName = (fullName) => {
	if (!fullName) return ""
	var versionItems = fullName.split("_")
	if (versionItems && versionItems.length >= 11) {
		return versionItems[0] + "_" + versionItems[1] + "_" + versionItems[2] + "_" + versionItems[3] + "_" +
			versionItems[4] + " " + versionItems[5] + " " + versionItems[6]
		// return versionItems[6] + "_" + versionItems[9] + "_" + versionItems[10].replace(".zip", "") + "（" + versionItems[1] + "）"
	}
}
 
 
/**
 * 获取升级文件硬件版本
 * @param {升级文件全称} fullName 
 */
const getHardwareName = (fullName) => {
	if (!fullName) return ""
	var versionItems = fullName.split("_")
	if (versionItems && versionItems.length >= 11) {
		return versionItems[0] + "_" + versionItems[1] + "_" + versionItems[2] + "_" + versionItems[3] + "_" +
			versionItems[4] + " " + versionItems[5]
	}
}

/**
 * 获取升级文件固件版本
 * @param {升级文件全称} fullName 
 */
const getFirmwareName = (fullName) => {
	if (!fullName) return ""
	var versionItems = fullName.split("_")
	if (versionItems && versionItems.length >= 11) {
		return versionItems[6]
	}
}

/**
 * 清除缓存
 */
const clearCache = () =>{
	currentLockInfoMap = new Map()
}






/**
 * 
 * @param {*} plugin 
 */
const loadNBInfo = (plugin) => {

	var isFunc = (fn) => typeof fn === "function";
	var isPromise = (likePromise) =>
		likePromise && "then" in likePromise && isFunc(likePromise.then);
	// 判断该次response是否为有效的
	var checkATResultIsSuccess = (res) => {
		console.log(Date.now(), " checkATResultIsSuccess res: ", res);
		var {
			errCode,
			data
		} = res;
		// 确定response是否返回相应的数据，可以通过匹配response相应的格式判断
		// 这里只做示例，具体需要根据业务及精确度的需要来确定正则表达式，
		// 注意：有些命令的response分好几个包返回，匹配前需要先处理好拼包
		var regexp =
		/^\s+\+.*\:.*(\r\n)+(OK)+\s+$/; // 匹配response文本的格式（带'+'号和当前命令）。如： " +CFUN:1 OK "、" +CGSN:865462042532636 OK "
		// var regexp = /^\s+.*(\r\n)+(OK)+\s+$/; // 匹配response文本的格式。如： " 460111173415918 OK "
		return errCode === "01" && regexp.test(data.response);
	};
	// 由于发送AT指令时，NB模组的状态不一定处于可正常返回命令的状态，所以需要最好加入重发机制
	var retry = (fn, count = 15, onResolve, onReject) => {
		if (count > 0) {
			var promise = fn();
			if (isPromise(promise)) {
				promise
					.then((res) => {
						var isSuccess = true;
						if (isSuccess) {
							isFunc(onResolve) && onResolve(res);
						} else {
							retry(fn, --count, onResolve, onReject);
						}
					})
					.catch((err) => {
						isFunc(onReject) && onReject(err);
					});
			}
		}
	};
	
	var testAT = (cmd) => {

		return new Promise((resolve, reject) => {
			retry(
				() =>
				plugin.sendATCmd({
					cmd,
				}),
				15,
				(res) => {
					console.log("onResolve res: ", res);
					if (resolve) {
						resolve(res)
					}
				},
				(err) => {
					console.log("onReject err: ", err);
					if (reject) {
						reject(err)
					}
				}
			);
		})
	};
	
	var imsi = ""
	var imei = ""
	
	return new Promise((resolve, reject)=>{
		plugin.startATCmdMode({
			timeout: 255
		}).then((res) => {
			
			// 注意：不建议同时调用多个不同的命令
			return testAT("AT+CIMI\r\n"); // 物联网卡号
			
			// testAT("AT+CGSN=1\r\n"); // 获取IMEI号
			// testAT("AT+CSQ\r\n");  // 获取信号质量
			// myPlugin.exitATCmdMode(); // 最后，在不需要做AT交互的情况下退出AT模式
		}).then(res1=>{
			console.log(res1)
			imsi = res1.data.response
			if(res1.data.response){
				imsi = imsi.replace("OK", "")
				imsi = imsi.replace(/[\r\n]/g, "")
			}
			console.log(imsi)
			return testAT("AT+CGSN=1\r\n")
		}).then(res2=>{
			imei = res2.data.response
			if(res2.data.response){
				var list = imei.split("\"")
				if(list.length > 2){
					imei = list[1]
				}
			}
			resolve({
				imsi,
				imei
			})
			
			plugin.exitATCmdMode(); // 最后，在不需要做AT交互的情况下退出AT模式
		}, error=>{
			reject(error)
		})
	})
	
	
}

export {
	doOperate,
	connect,
	disconnect,
	openDoorByRemote,
	queryLockInfo,
	getUploadFileShortName,
	getHardwareName,
	getFirmwareName,
	clearCache,
	loadNBInfo
}
